﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using FarseerGames.FarseerPhysics.Mathematics;
using System.Collections;
using System.Diagnostics;

namespace CymbergajTouch.Match
{
    /// <summary>
    /// Gets input data and applies it to movables.
    /// </summary>
    class InputManager
    {
        public HashSet<Movable> movables = new HashSet<Movable>();
        private Hashtable activeDrags = new Hashtable();
        private Form form;
        private Windows7.Multitouch.TouchHandler handler;

        private static InputManager instance;

        private InputManager()
        {
            // hide constructor
        }

        public static InputManager Instance
        {
            get
            {
                if (instance == null)
                    instance = new InputManager();
                return instance;
            }
        }

        /// <summary>
        /// Initializes mouse/touchpad input.
        /// </summary>
        /// <param name="handle"></param>
        public void InitializeInput(IntPtr handle)
        {
            if (handler != null)
            {
                return;
            }
            form = (Form)Form.FromHandle(handle);
            try
            {
                handler = Windows7.Multitouch.WinForms.Factory.CreateHandler<Windows7.Multitouch.TouchHandler>(form);
                handler.TouchDown += new EventHandler<Windows7.Multitouch.TouchEventArgs>(TouchDown);
                handler.TouchMove += new EventHandler<Windows7.Multitouch.TouchEventArgs>(TouchMove);
                handler.TouchUp += new EventHandler<Windows7.Multitouch.TouchEventArgs>(TouchUp);
                Console.WriteLine("touch listeners added");
            }
            catch (System.EntryPointNotFoundException)
            {
                Debug.WriteLine("Multitouch not served.");
            }

            form.MouseDown += new MouseEventHandler(MouseDown);
            form.MouseMove += new MouseEventHandler(MouseMove);
            form.MouseUp += new MouseEventHandler(MouseUp);

        }

        /// <summary>
        /// Adds movable.
        /// </summary>
        /// <param name="movable">movable</param>
        public void AddMovable(Movable movable)
        {
            movables.Add(movable);
        }

        /// <summary>
        /// Removes movable.
        /// </summary>
        /// <param name="movable">movable</param>
        public void RemoveMovable(Movable movable)
        {
            if (activeDrags.ContainsValue(movable))
            {
                int removingMovableDragId = -2;
                foreach (int key in activeDrags.Keys)
                {
                    if (activeDrags[key] == movable)
                    {
                        removingMovableDragId = key;
                        break;
                    }
                }
                StopDrag(removingMovableDragId);
            }
            movables.Remove(movable);
        }

        /// <summary>
        /// Method for mouse down event.
        /// </summary>
        /// <param name="sender">sender</param>
        /// <param name="e">args</param>
        public void MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                StartDrag(-1, new Vector2(e.X, e.Y));
            }
        }

        /// <summary>
        /// Method for mouse move event.
        /// </summary>
        /// <param name="sender">sender</param>
        /// <param name="e">args</param>
        public void MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                ContinueDrag(-1, new Vector2(e.X, e.Y));
            }
        }

        /// <summary>
        /// Method for mouse up event.
        /// </summary>
        /// <param name="sender">sender</param>
        /// <param name="e">args</param>
        public void MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                StopDrag(-1);
            }
        }

        /// <summary>
        /// Method for touch down event.
        /// </summary>
        /// <param name="sender">sender</param>
        /// <param name="e">args</param>
        public void TouchDown(object sender, Windows7.Multitouch.TouchEventArgs e)
        {
            StartDrag(e.Id, new Vector2(e.Location.X, e.Location.Y));
        }

        /// <summary>
        /// Method for touch move event.
        /// </summary>
        /// <param name="sender">sender</param>
        /// <param name="e">args</param>
        public void TouchMove(object sender, Windows7.Multitouch.TouchEventArgs e)
        {
            ContinueDrag(e.Id, new Vector2(e.Location.X, e.Location.Y));
        }

        /// <summary>
        /// Method for touch up event.
        /// </summary>
        /// <param name="sender">sender</param>
        /// <param name="e">args</param>
        public void TouchUp(object sender, Windows7.Multitouch.TouchEventArgs e)
        {
            StopDrag(e.Id);
        }

        /// <summary>
        /// Attempts to start object move.
        /// </summary>
        /// <param name="id">drag id for touch pad, -1 for mouse</param>
        /// <param name="position">click position</param>
        private void StartDrag(int id, Vector2 position)
        {
            List<Movable> movablesCopy = new List<Movable>(movables);
            foreach (Movable movable in movablesCopy)
            {
                if (movable.IsTouched(position))
                {
                    if (!activeDrags.Contains(movable))
                    {
                        movable.Grab(position);
                        activeDrags[id] = movable;
                    }
                }
            }
        }

        /// <summary>
        /// Continues object move.
        /// </summary>
        /// <param name="id">drag id for touch pad, -1 for mouse</param>
        /// <param name="position">mouse/touch position</param>
        private void ContinueDrag(int id, Vector2 position)
        {
            Movable movable = (Movable)activeDrags[id];
            if (movable != null)
            {
                movable.Move(position);
            }
        }

        /// <summary>
        /// Stops object move.
        /// </summary>
        /// <param name="id">drag id for touch pad, -1 for mouse</param>
        private void StopDrag(int id)
        {
            Movable movable = (Movable)activeDrags[id];
            if (movable != null)
            {
                movable.Drop();
                activeDrags.Remove(id);
            }
        }
    }
}
